import * as fs from 'fs';
import type { Token } from './types';
import { keywords, operators, delimiters } from './lib/word';

// 加载JavaScript代码
const code: string = fs.readFileSync('./test/testCode.js', 'utf-8');

/**
 * 词法分析器函数
 * 对输入的JavaScript代码字符串进行分析，生成Token数组。
 *
 * @param input 输入的JavaScript代码字符串。
 * @returns 生成的Token数组，每个Token代表代码中的一个最小单位，如关键词、运算符、标识符等。
 */
function tokenize(input: string): Token[] {
  // 初始化位置变量和用于存储生成Token的数组
  let position = 0;
  const tokens: Token[] = [];
  // 遍历输入代码字符串，直到处理完所有字符
  while (position < input.length) {
    // 跳过并前进到下一个非空白字符
    if (/\s/.test(input[position])) {
      position++;
      continue;
    }

    // 检查当前字符是否位于预定义关键词列表中，如果是则生成对应的Token
    if (keywords.some((keyword) => input.startsWith(keyword, position))) {
      const keyword = keywords.find((k) => input.startsWith(k, position));
      tokens.push({ kind: 'keyword', value: keyword as string });
      position += keyword!.length;
      continue;
    }

    // 处理单行注释：找到以'//'开始的注释并生成Token
    if (input.substring(position, position + 2) === '//') {
      let endOfLine = input.indexOf('\r\n', position);
      // 若在行尾未找到换行符，则表示注释至字符串末尾
      if (endOfLine === -1) {
        endOfLine = input.length;
      }
      tokens.push({
        kind: 'comment',
        value: input.slice(position + 2, endOfLine),
      });
      position = endOfLine;
      continue;
    }

    // 处理多行注释：找到以'/*'开始的注释并生成Token
    if (input.substring(position, 2) === '/*') {
      let endOfComment = input.indexOf('*/', position + 2);
      // 若未找到注释结束符，则抛出错误
      if (endOfComment === -1) {
        throw new Error('Unterminated multi-line comment');
      }
      tokens.push({
        kind: 'comment',
        value: input.slice(position + 2, endOfComment),
      });
      position = endOfComment + 2;
      continue;
    }

    // 检查当前字符是否是预定义运算符之一，如果是则生成对应的Token
    if (operators.some((op) => input[position] === op)) {
      const operator = input[position];
      tokens.push({ kind: 'operator', value: operator });
      position++;
      continue;
    }

    // 检查当前字符是否是预定义的分隔符之一，如果是则生成对应的Token
    if (delimiters.some((delim) => input[position] === delim)) {
      const delimiter = input[position];
      tokens.push({ kind: 'delimiter', value: delimiter });
      position++;
      continue;
    }

    // 处理字符串：找到字符串开始的引号，读取并生成Token
    if (input[position] === '"' || input[position] === "'") {
      const quote = input[position++];
      let stringValue = '';
      // 循环读取直到遇到与开始引号相匹配的结束引号
      while (input[position] !== quote && position < input.length) {
        stringValue += input[position++];
      }
      // 验证是否成功找到结束引号并生成Token
      if (input[position] === quote) {
        position++; // 移动到下一个字符，跳过结束引号
        tokens.push({ kind: 'string', value: stringValue });
      } else {
        throw new Error('Unterminated string');
      }
      continue;
    }

    // 处理标识符：从字母、下划线或美元符号开始，连续读取符合标识符规则的字符并生成Token
    if (/^[a-zA-Z_$]/.test(input[position])) {
      let identifier = '';
      while (
        /^[a-zA-Z0-9_$]/.test(input[position]) &&
        position < input.length
      ) {
        identifier += input[position++];
      }
      tokens.push({ kind: 'identifier', value: identifier });
      continue;
    }

    // 处理数字：检查当前位置是否存在数字，提取并生成Token
    if (/^-?[0-9]+(\.[0-9]*)?$/g.test(input[position])) {
      const numberMatch = /^-?[0-9]+(\.[0-9]*)?/.exec(input.slice(position));
      // 如果找到了数字表达式，将其转换为浮点数并生成Token
      if (numberMatch) {
        const numberValue = parseFloat(numberMatch[0]);
        tokens.push({ kind: 'number', value: numberValue });
        position += numberMatch[0].length;
      }
      continue;
    }

    // 当前位置的字符无法识别时，抛出错误
    throw new Error(
      `Unrecognized token << ${input[position]} >> at position ${position}`
    );
  }

  // 返回生成的Token数组
  return tokens;
}

// 使用示例
try {
  const tokens = tokenize(code);
  console.log(tokens);
  // 统计词频
  const wordFrequency = tokens.reduce((acc: any, token) => {
    acc[token.kind] = (acc[token.kind] || 0) + 1;
    return acc;
  }, {});
  console.log(wordFrequency);
} catch (error: any) {
  console.error(error.message);
}
